/*******************************************************************************
 * Copyright 2011, 2012 Chris Banes.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/
package uk.co.senab2.photoview2;

import com.ryan.ohos.extension.gesture.GestureDetector;

import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.Image;
import ohos.agp.components.PageSlider;
import ohos.agp.components.element.Element;
import ohos.agp.components.element.PixelMapElement;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.PixelMapHolder;
import ohos.agp.utils.Matrix;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.global.resource.Resource;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;
import ohos.multimodalinput.event.TouchEvent;

import uk.co.senab2.photoview2.PhotoViewAttacher.OnMatrixChangedListener;
import uk.co.senab2.photoview2.PhotoViewAttacher.OnPhotoTapListener;
import uk.co.senab2.photoview2.PhotoViewAttacher.OnViewTapListener;
import uk.co.senab2.photoview2.log.LogManager;

/**
 * PhotoView.
 *
 * @author author
 * @version version
 */
public class PhotoView extends Image implements IPhotoView, Component.BindStateChangedListener, Component.DrawTask {

    private static final String TAG = "PhotoView";

    private Element mElement;

    private PixelMapHolder mPixelMapHolder;

    private PixelMap mPixelMap;

    private Paint mPaint;

    private ScaleMode mPendingScaleType;

    private Matrix mMatrix;

    private Matrix mDrawMatrix = null;

    private boolean isChange = false;

    protected PhotoViewAttacher mAttacher;

    public PhotoView(Context context) {
        this(context, null);
    }

    public PhotoView(Context context, AttrSet attrSet) {
        this(context, attrSet, "");
    }

    public PhotoView(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        //  super.setScaleType(ScaleType.MATRIX);
        init();
    }

    /**
     * init.
     */
    protected void init() {
        if (mElement != null) {
            isChange = true;
            mPixelMap = Compat.elementToPixelMap(mElement);
        }
        if (null == mAttacher || null == mAttacher.getImageView()) {
            mAttacher = new PhotoViewAttacher(this);
        }
        if (mMatrix == null) {
            mMatrix = new Matrix();
        }
        if (mPaint == null) {
            mPaint = new Paint();
        }
        mPendingScaleType = super.getScaleMode();
        if (mPendingScaleType != null) {
            if (mPendingScaleType != ScaleMode.ZOOM_CENTER) {
                setScaleMode(mPendingScaleType);
            }
            mPendingScaleType = null;
        }
        setBindStateChangedListener(this);
        addDrawTask(this);
    }

    /**
     * setImageMatrix .
     *
     * @param matrix matrix
     */
    public void setImageMatrix(Matrix matrix) {
        // collapse null and identity to just null
        if (matrix != null && matrix.isIdentity()) {
            matrix = null;
        }

        // don't invalidate unless we're actually changing our matrix
        if (matrix == null && !mMatrix.isIdentity() ||
                matrix != null && !mMatrix.equals(matrix)) {
            mMatrix.setMatrix(matrix);
            configureBounds();
            invalidate();
        }
    }

    /**
     * getImageMatrix .
     *
     * @return Matrix
     */
    public Matrix getImageMatrix() {
        if (mAttacher != null) {
            return mAttacher.getImageMatrix();
        }
        return null;
    }

    /**
     * setPageSlider .
     *
     * @param pageSlider pageSlider
     */
    public void setPageSlider(PageSlider pageSlider) {
        if (mAttacher != null) {
            mAttacher.setPageSlider(pageSlider);
        }
    }

    @Override
    public void setRotationTo(float rotationDegree) {
        mAttacher.setRotationTo(rotationDegree);
    }

    @Override
    public void setRotationBy(float rotationDegree) {
        mAttacher.setRotationBy(rotationDegree);
    }

    @Override
    public boolean canZoom() {
        return mAttacher.canZoom();
    }

    @Override
    public RectFloat getDisplayRect() {
        return mAttacher.getDisplayRect();
    }

    @Override
    public void getDisplayMatrix(Matrix matrix) {
        mAttacher.getDisplayMatrix(matrix);
    }

    @Override
    public boolean setDisplayMatrix(Matrix finalRectangle) {
        return mAttacher.setDisplayMatrix(finalRectangle);
    }

    @Override
    public float getMinimumScale() {
        return mAttacher.getMinimumScale();
    }

    @Override
    public float getMediumScale() {
        return mAttacher.getMediumScale();
    }

    @Override
    public float getMaximumScale() {
        return mAttacher.getMaximumScale();
    }

    @Override
    public float getScaleFloat() {
        return mAttacher.getScaleFloat();
    }

    @Override
    public ScaleMode getScaleType() {
        return mAttacher.getScaleType();
    }


    @Override
    public void setAllowParentInterceptOnEdge(boolean allow) {
        mAttacher.setAllowParentInterceptOnEdge(allow);
    }

    @Override
    public void setMinimumScale(float minimumScale) {
        mAttacher.setMinimumScale(minimumScale);
    }

    @Override
    public void setMediumScale(float mediumScale) {
        mAttacher.setMediumScale(mediumScale);
    }

    @Override
    public void setMaximumScale(float maximumScale) {
        mAttacher.setMaximumScale(maximumScale);
    }

    @Override
    public void setScaleLevels(float minimumScale, float mediumScale, float maximumScale) {
        mAttacher.setScaleLevels(minimumScale, mediumScale, maximumScale);
    }


    @Override
    public void setImageElement(Element element) {
        if (element == null) {
            return;
        }
        mElement = element;
        mPixelMap = Compat.elementToPixelMap(element);
        isChange = true;
        configureBounds();
        invalidate();

        if (mAttacher != null) {
            mAttacher.update();
        }
    }

    @Override
    public Element getImageElement() {
        return mElement;
    }


    @Override
    public void setImageAndDecodeBounds(int resId) {
        if (resId == 0) {
            return;
        }

        try {
            ImageSource.SourceOptions options = new ImageSource.SourceOptions();
            Resource resource = getContext().getResourceManager().getResource(resId);
            ImageSource imageSource = ImageSource.create(resource, options);
            ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions();
            setPixelMap(imageSource.createPixelmap(decodingOptions));
        } catch (Exception exception) {
            LogManager.getLogger().e(TAG, exception.getMessage());
        }
    }

    @Override
    public void setPixelMap(PixelMap pixelMap) {
        if (pixelMap == null) {
            return;
        }

        mElement = new PixelMapElement(pixelMap);
        mPixelMap = pixelMap;
        isChange = true;
        configureBounds();
        invalidate();

        if (mAttacher != null) {
            mAttacher.update();
        }

    }

    @Override
    public void setPixelMap(int resId) {
        setImageAndDecodeBounds(resId);
    }

    @Override
    public PixelMap getPixelMap() {
        return mPixelMap;
    }

    @Override
    public void setOnMatrixChangeListener(OnMatrixChangedListener listener) {
        mAttacher.setOnMatrixChangeListener(listener);
    }

    @Override
    public void setOnLongClickListener(LongClickedListener listener) {
        mAttacher.setOnLongClickListener(listener);
    }

    @Override
    public void setOnPhotoTapListener(OnPhotoTapListener listener) {
        mAttacher.setOnPhotoTapListener(listener);
    }

    @Override
    public void setOnViewTapListener(OnViewTapListener listener) {
        mAttacher.setOnViewTapListener(listener);
    }

    @Override
    public void setScale(float scale) {
        mAttacher.setScale(scale);
    }

    @Override
    public void setScale(float scale, boolean animate) {
        mAttacher.setScale(scale, animate);
    }

    @Override
    public void setScale(float scale, float focalX, float focalY, boolean animate) {
        mAttacher.setScale(scale, focalX, focalY, animate);
    }

    @Override
    public void setScaleType(ScaleMode scaleType) {
        if (null != mAttacher) {
            mAttacher.setScaleType(scaleType);
        } else {
            mPendingScaleType = scaleType;
        }
    }

    @Override
    public void setScaleMode(ScaleMode scaleMode) {
        if (mAttacher == null) {
            mPendingScaleType = scaleMode;
        } else {
            mAttacher.setScaleType(scaleMode);
        }
    }

    @Override
    public ScaleMode getScaleMode() {
        return mAttacher.getScaleType();
    }

    @Override
    public void setZoomable(boolean zoomable) {
        mAttacher.setZoomable(zoomable);
    }

    @Override
    public PixelMap getVisibleRectangleBitmap() {
        return mAttacher.getVisibleRectangleBitmap();
    }

    @Override
    public void setZoomTransitionDuration(int milliseconds) {
        mAttacher.setZoomTransitionDuration(milliseconds);
    }

    @Override
    public IPhotoView getIPhotoViewImplementation() {
        return mAttacher;
    }

    @Override
    public void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener newOnDoubleTapListener) {
        mAttacher.setOnDoubleTapListener(newOnDoubleTapListener);
    }

    @Override
    public void setOnScaleChangeListener(PhotoViewAttacher.OnScaleChangeListener onScaleChangeListener) {
        mAttacher.setOnScaleChangeListener(onScaleChangeListener);
    }

    @Override
    public void setOnSingleFlingListener(PhotoViewAttacher.OnSingleFlingListener onSingleFlingListener) {
        mAttacher.setOnSingleFlingListener(onSingleFlingListener);
    }

    @Override
    public boolean dispatchTouchEvent(TouchEvent event) {
        return false;
    }

    /**
     * resetMatrix.
     */
    public void resetMatrix() {
        if (mAttacher != null) {
            mAttacher.cleanup();
            mAttacher.resetMatrix();
        }
    }

    @Override
    public void onComponentBoundToWindow(Component component) {
        init();
    }

    @Override
    public void onComponentUnboundFromWindow(Component component) {
        if (mPixelMap != null) {
            mPixelMap.release();
        }
        if (mPixelMapHolder != null) {
            mPixelMapHolder.release();
        }
        mPixelMapHolder = null;
        mPixelMap = null;
        mElement = null;
        onDetachedFromWindow();
    }

    /**
     * onDetachedFromWindow.
     */
    protected void onDetachedFromWindow() {

    }

    private void configureBounds() {
        if (mElement == null) {
            return;
        }

        final int dwidth = mElement.getWidth();
        final int dheight = mElement.getHeight();

        final int vwidth = getWidth() - getPaddingLeft() - getPaddingRight();
        final int vheight = getHeight() - getPaddingTop() - getPaddingBottom();

        final boolean fits = (dwidth < 0 || vwidth == dwidth)
                && (dheight < 0 || vheight == dheight);

        if (dwidth <= 0 || dheight <= 0) {
            /* If the drawable has no intrinsic size, or we're told to
                scaletofit, then we just fill our entire view.
            */

            mElement.setBounds(0, 0, vwidth, vheight);
            mDrawMatrix = null;
        } else {
            // We need to do the scaling ourself, so have the drawable
            // use its native size.
            mElement.setBounds(0, 0, dwidth, dheight);

            if (mMatrix.isIdentity()) {
                mDrawMatrix = null;
            } else {
                mDrawMatrix = mMatrix;
            }


        }
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        onDrawImage(canvas);
    }

    /**
     * onDrawImage
     *
     * @param canvas canvas
     */
    protected void onDrawImage(Canvas canvas) {
        if (mElement == null || mPixelMap == null) {
            // couldn't resolve the URI
            return;
        }

        if (mDrawMatrix == null && getPaddingTop() == 0 && getPaddingLeft() == 0) {
            mElement.drawToCanvas(canvas);
        } else {

            canvas.save();

            canvas.translate(getPaddingLeft(), getPaddingTop());

            if (mDrawMatrix != null) {
                canvas.concat(mDrawMatrix);
            }

            if (mPixelMapHolder == null || isChange) {
                mPixelMapHolder = new PixelMapHolder(mPixelMap);
                isChange = false;
            }
            canvas.drawPixelMapHolder(mPixelMapHolder, 0, 0, mPaint);

            canvas.restore();

        }
    }

}
